package com.cars.nfp.service;

import com.cars.nfp.dto.CanAccessDTO;
import com.cars.nfp.feign.UserAppClient;
import com.cars.util.global.GlobalReturnCode;
import com.cars.util.json.JsonResult;
import com.cars.util.json.JsonResultUtil;
import com.cars.util.jwt.JwtResult;
import com.cars.util.jwt.JwtUtil;
import com.crl.redis.RedisKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * 网关验证token权限
 *
 * @author 宋长军
 * @date 2019/8/16 9:44
 */
@Service
public class AuthorizedService {

    @Value("${permitUrl}")
    private String permitUrl;
    private List<String> permitUrlList = new ArrayList<>();
    @Value("${authorizedAppName}")
    private String authorizedAppName;
    @Autowired
    private AuthRedisService authRedisService;
    @Autowired
    private UserAppClient userAppClient;

    /**
     * 判断token是否允许访问接口
     *
     * @param canAccessDTO 请求信息
     * @return 有权限访问返回true，否则返回false
     */
    public JsonResult canAccess(CanAccessDTO canAccessDTO) {

        String apiUrl = canAccessDTO.getApiUrl();
        String token = canAccessDTO.getToken();

        if (isPermitUrl(apiUrl)) {
            return JsonResultUtil.ok();
        }

        // 获取jwt解析数据
        JwtResult jwtResult = JwtUtil.parseJWT(token.trim());
        boolean isValid = jwtResult.isValid();

        // 判断JWT是否有效，以及JWT中携带的accessToken对否有效
        if (!isValid || !isValidAccessToken(jwtResult)) {
            return new JsonResult(GlobalReturnCode.INVALID_TOKEN);
        }

        String username = jwtResult.getOwner();
        // 判断是否有访问权限
        if (!isAuthorizedApi(username, canAccessDTO)) {
            return new JsonResult(GlobalReturnCode.NO_PERMISSION_TOKEN);
        }
        return JsonResultUtil.ok();
    }

    /**
     * 判断是否为不鉴权的请求
     * <p>
     * 网关转发来的url带有application name
     *
     * @param apiUrl 请求url
     * @return true：不鉴权
     */
    private boolean isPermitUrl(String apiUrl) {

        if (CollectionUtils.isEmpty(permitUrlList)) {
            permitUrlList = Arrays.asList(permitUrl.split(","));
        }
        return permitUrlList.stream()
                // 过滤鉴权服务的非鉴权路径
//                .map(url -> authorizedAppName + url)
                .anyMatch(apiUrl::equals);
    }

    /**
     * 根据用户信息返回用户已经被授权的url列表
     *
     * @param username username
     * @return true有权限
     */
    private boolean isAuthorizedApi(String username, CanAccessDTO canAccessDTO) {

        List<String> userAuthList = authRedisService.getUserAuth(username);

        if (CollectionUtils.isEmpty(userAuthList)) {

            // 获取用户权限
            JsonResult jsonResult = userAppClient.auth(username);
            if (jsonResult.getReturnCode().equals(GlobalReturnCode.OK.getReturnCode())) {

                Map<String, Object> map = (Map<String, Object>) jsonResult.getData();
                userAuthList = (List<String>) map.get("resources");
            }
            if (CollectionUtils.isEmpty(userAuthList)) {
                userAuthList = Collections.singletonList("Unauthorization");
            }
            authRedisService.saveUserAuth(username, userAuthList);
        }
        return userAuthList.stream()
                .anyMatch(canAccessDTO.getApiUrl()::equals);
    }

    /**
     * 判断jwt携带的accessToken是否有效
     *
     * @param jwtResult jwtResult
     * @return true：有效
     */
    private boolean isValidAccessToken(JwtResult jwtResult) {

        if (authRedisService.isPresence(RedisKey.HGP_USER_ACCESSTOKEN, jwtResult.getOwner())) {

            List<String> redisAccessToken = authRedisService.getUserAccessToken(jwtResult.getOwner());
            if (!CollectionUtils.isEmpty(redisAccessToken)) {
                return redisAccessToken.stream().anyMatch(jwtResult.getToken()::equals);
            }
        }
        return false;
    }
}
